#include "c_jsonparser.h"
#include <string.h>

jPtr __json_obj(const char**);
jPtr __json_list(const char**);
char* __parseString(const char**);
char* __parseNumStr(const char**);
int __isFloat(const char*);


void __consume_spaces(const char **c_str){
	while(**c_str != '\0' && (**c_str == ' ' || **c_str == '\n' || **c_str == '\t'
		|| **c_str == '\r' || **c_str == ':' || **c_str == ','))
		(*c_str)++;
}


jPtr __json_obj(const char **c_ptr){
	jPtr object, obj;
	int o_pos;
	char *buffer;

	o_pos = 0;

	if(c_ptr == 0)
		return 0;
	object = (jPtr)malloc(sizeof(struct json));
	//object->data = (jPtr)malloc(sizeof(struct json));
	//object->data_type = Object;
	(*c_ptr)++;
	while(**c_ptr != '}' && **c_ptr != '\0'){
		__consume_spaces(c_ptr);
		object[o_pos].key = __parseString(c_ptr);
		__consume_spaces(c_ptr);
		switch(**c_ptr){
		case '\"':
			object[o_pos].data_type = String;
			object[o_pos].string = __parseString(c_ptr);
			break;
		case '[':
			object[o_pos].data_type = List;
			object[o_pos].data = __json_list(c_ptr);
			break;
		case '{':
			object[o_pos].data_type = Object;
			object[o_pos].data = __json_obj(c_ptr);
			break;
		default:
			buffer = __parseNumStr(c_ptr);
			if(__isFloat(buffer) == 0){
				object[o_pos].data_type = Integer;
				object[o_pos].i = atoi(buffer);
			}
			else{
				object[o_pos].data_type = Float;
				object[o_pos].d = atof(buffer);
			}
			free(buffer);
			break;
		}
		o_pos++;
		obj = (jPtr)malloc(sizeof(struct json)*(o_pos+1));
		memcpy_s(obj, sizeof(struct json)*(o_pos+1), object, sizeof(struct json)*o_pos);
		free(object);
		object = obj;
	}
	object[o_pos].data_type = end;
	(*c_ptr)++;
	return object;
}


jPtr __json_list(const char **c_str){
	jPtr list, obj;
	int it;
	char *buffer;

	it = 0;
	list = (jPtr)malloc(sizeof(struct json));
	list->data_type = List;
	list->list_type = _undef;
	list->data = (jPtr)malloc(sizeof(struct json));

	(*c_str)++;
	while(**c_str != '\0' && **c_str != ']'){
		__consume_spaces(c_str);
		if(list->list_type == _undef){
			switch(**c_str){
			case '\"':
				list->list_type = String;
				break;
			case '{':
				list->list_type = Object;
				break;
			case '[':
				list->list_type = List;
				break;
			default:
				list->list_type = Numeric;
				break;
			}
			continue;
		}
		else{
			switch(list->list_type){
			case String:
				list->data[it].data_type = String;
				list->data[it].string = __parseString(c_str);
				break;
			case Object:
				list->data[it].data_type = Object;
				list->data[it].data = __json_obj(c_str);
				break;
			case List:
				list->data[it].data_type = List;
				list->data[it].data = __json_list(c_str);
				break;
			case Integer:
				list->data[it].data_type = Integer;
				list->data[it].i = atoi(__parseNumStr(c_str));
				break;
			case Float:
				list->data[it].data_type = Float;
				list->data[it].d = atof(__parseNumStr(c_str));
				break;
			case Numeric:
				buffer = __parseNumStr(c_str);
				if(__isFloat(buffer) == 0){
					list->list_type = Integer;
					list->data[it].data_type = Integer;
					list->data[it].i = atoi(buffer);
				}
				else{
					list->list_type = Float;
					list->data[it].data_type = Float;
					list->data[it].d = atof(buffer);
				}
				free(buffer);
				break;
			}
		}
		it++;
		obj = (jPtr)malloc(sizeof(struct json)*(it+1));
		memcpy_s(obj, sizeof(struct json)*(it+1), list->data, sizeof(struct json)*it);
		free(list->data);
		list->data = obj;
	}
	list->data[it].data_type = end;
	(*c_str)++;
	return list;
}


char* __parseString(const char **c_str){
	char *buffer;
	int it;

	it = 0;
	buffer = (char*)malloc(it+1);
	(*c_str)++;
	while(**c_str != '\0' && **c_str != '\"'){
		if(**c_str == '\\' && *((*c_str)+1) == '\"'){
			buffer[it] = '\"';
			(*c_str)++;
		}
		else
			buffer[it] = **c_str;
		it++;
		(*c_str)++;
		buffer = (char*)realloc(buffer, it+1);
	}
	(*c_str)++; //care this could produce a mem error in case the string is at the end!!
	buffer[it] = '\0';
	return buffer;
}


char* __parseNumStr(const char **c_str){
	char *buffer;
	int it;

	it = 0;
	buffer = (char*)malloc(it+1);
	while(**c_str != '\0' && **c_str != ',' && **c_str != ' '){
		buffer[it] = **c_str;
		(*c_str)++;
		it++;
		buffer = (char*)realloc(buffer, it+1);
	}
	buffer[it] = '\0';
	return buffer;
}


int __isFloat(const char *c_str){
	int i;
	for(i = 0; c_str[i] != '\0'; i++)
		if(c_str[i] == '.')
			return 1;
	return 0;
}


jPtr _parseJsonStr(const char *c_str){
	jPtr result;
	char *ptr, **it;

	if(c_str == 0)
		return 0;
	ptr = (char*)c_str;
	it = &ptr;
	result = 0;
	 __consume_spaces(it);
	if(**it == '[')
		result = __json_list(it);
	else if (**it == '{')
		result = __json_obj(it);
	return result;
}